/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cardgame;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import cardgame.game.*;
import cardgame.game.commands.*;

/**
 *
 * @author Carl
 */
public class PlayerCardFrontend{

    public PlayerCardFrontend(CommandSender commandSender, CardChooserManager cardChooserManager, PanGame panGame, PanCardInfo panCardInfo){
        this.commandSender = commandSender;
        this.cardChooserManager = cardChooserManager;
        this.panGame = panGame;
        this.panCardInfo = panCardInfo;
    }
    private int playerIndex;
    private CardChooserManager cardChooserManager;
    private CommandSender commandSender;
    private PanGame panGame;
    private PanCardInfo panCardInfo;
    private boolean isEnabled;
    private PanCard_Deck panCard_Deck;
    private PanCard panCard_Graveyard;
    private PanCard[] panCards_Hand;
    private PanCard[] panCards_Monsters;
    private PanCard[] panCards_ManaPool;
    private HashMap<PanCard, FrontendCardInfo> buttonsCardsMap = new HashMap<PanCard, FrontendCardInfo>();
    private Card topGraveyardCard;
    private JPopupMenu popupMenuSelectedCard = new JPopupMenu();
    
    public void update(Player player){
        panCard_Deck.setSize(player.getDeck().size());
        //Hand
        removeCardsPanels(panCards_Hand);
        panGame.initaliseCardPanels_Hand(this, playerIndex, player.getHand().size());
        updateCardPanels(panCards_Hand, player.getHand());
        for(int i=0;i<panCards_Hand.length;i++){
            panCards_Hand[i].setIsVisibleToUser(playerIndex == panGame.getPlayerIndex());
        }
        //Monsters
        removeCardsPanels(panCards_Monsters);
        panGame.initaliseCardPanels_Monsters(this, playerIndex, player.getMonsters().size());
        updateCardPanels(panCards_Monsters, player.getMonsters());
        //ManaPool
        removeCardsPanels(panCards_ManaPool);
        panGame.initaliseCardPanels_ManaPool(this, playerIndex, player.getManaPool().size());
        updateCardPanels(panCards_ManaPool, player.getManaPool());
        topGraveyardCard = player.getGraveyard().getLast();
        panCard_Graveyard.setCard(topGraveyardCard);
    }
    
    private void removeCardsPanels(PanCard[] panCards){
        for(int i=0;i<panCards.length;i++){
            PanCard panCard = panCards[i];
            panCard.getParent().remove(panCard);
            buttonsCardsMap.remove(panCard);
        }
    }
    
    private void updateCardPanels(PanCard[] panCards, Cards cards){
        for(int i=0;i<panCards.length;i++){
            updateCardPanel(panCards[i], cards.get(i));
        }
    }
    
    private void updateCardPanel(PanCard panCard, Card card){
        FrontendCardInfo cardInfo = getCardInfo(panCard);
        cardInfo.setCard(card);
        panCard.setCard(card);
    }

    public void setPanCard_Deck(PanCard_Deck panCard_Deck){
        this.panCard_Deck = panCard_Deck;
    }

    public void setPanCard_Graveyard(final PanCard panCard_Graveyard){
        this.panCard_Graveyard = panCard_Graveyard;
        panCard_Graveyard.addMouseListener(new MouseAdapter(){

            @Override
            public void mouseEntered(MouseEvent evt){
                super.mouseEntered(evt);
                if(topGraveyardCard != null){
                    panCardInfo.showCard(topGraveyardCard);
                }
            }

            @Override
            public void mouseExited(MouseEvent evt){
                super.mouseExited(evt);
                panCardInfo.reset();
            }
        });
    }

    public void setPanCards_Hand(PanCard[] panCards_Hand){
        this.panCards_Hand = panCards_Hand;
        initialisePanels(panCards_Hand, CardPosition.Zone.HAND);
    }

    public void setPanCards_Monsters(PanCard[] panCards_MonsterZone){
        this.panCards_Monsters = panCards_MonsterZone;
        initialisePanels(panCards_MonsterZone, CardPosition.Zone.MONSTER);
    }

    public void setPanCards_ManaPool(PanCard[] panCards_SpellZone){
        this.panCards_ManaPool = panCards_SpellZone;
        initialisePanels(panCards_SpellZone, CardPosition.Zone.MANA);
    }
    
    private void initialisePanels(PanCard[] panCards, CardPosition.Zone zone){
        for(int i=0;i<panCards.length;i++){
            final PanCard panCard = panCards[i];
            buttonsCardsMap.put(panCard, new FrontendCardInfo(new CardPosition(zone, i)));
            panCard.addMouseListener(new MouseAdapter(){

                @Override
                public void mouseClicked(MouseEvent evt){
                    super.mouseClicked(evt);
                    FrontendCardInfo cardInfo = getCardInfo(panCard);
                    if(cardInfo.getCard().getGame().isRunning()){
                        CardChooser activeCardChooser = cardChooserManager.getActiveCardChooser();
                        if(activeCardChooser != null){
                            if(activeCardChooser.isValidChoice(cardInfo.getCard())){
                                int cardIndex = cardInfo.getCardPosition().getIndex();
                                if(activeCardChooser instanceof ManaChooser){
                                    ManaChooser manaChooser = (ManaChooser) activeCardChooser;
                                    manaChooser.addManaProvider(cardInfo.getCard());
                                    if(manaChooser.isFinished()){
                                        switch(manaChooser.getType()){
                                            case MONSTER_SUMMON_COST:
                                                int handCardIndex_1 = manaChooser.getChooseInformation()[0];
                                                commandSender.sendCommand(new SummonMonsterCommand(handCardIndex_1, manaChooser.getManaIndices()));
                                                cardChooserManager.stop();
                                                break;

                                            case MONSTER_SPELL_COST:
                                                int monsterCardIndex = manaChooser.getChooseInformation()[0];
                                                int spellIndex = manaChooser.getChooseInformation()[1];
                                                MonsterCard monsterCard = (MonsterCard) getCardInfo(panCards_Monsters[monsterCardIndex]).getCard();
                                                Spell spell_1 = monsterCard.getSpells()[spellIndex];
                                                cardChooserManager.startChoosing_MonsterSpellParameter(spell_1, monsterCardIndex, spellIndex, manaChooser.getManaIndices());
                                                sendSpellCastIfChoosingHasFinished();
                                                break;

                                            case SPELL_COST:
                                                int handCardIndex_2 = manaChooser.getChooseInformation()[0];
                                                SpellCard spellCard = (SpellCard) getCardInfo(panCards_Hand[handCardIndex_2]).getCard();
                                                Spell spell_2 = spellCard.getSpell();
                                                cardChooserManager.startChoosing_SpellParameter(spell_2, handCardIndex_2, manaChooser.getManaIndices());
                                                sendSpellCastIfChoosingHasFinished();
                                                break;
                                        }
                                    }
                                }
                                else if(activeCardChooser instanceof SpellParameterChooser){
                                    SpellParameterChooser spellParameterChooser = (SpellParameterChooser) activeCardChooser;
                                    spellParameterChooser.setNextParameter(cardInfo.getCard());
                                    sendSpellCastIfChoosingHasFinished();
                                }
                                if(activeCardChooser instanceof AttackTargetChooser){
                                    AttackTargetChooser attackTargetChooser = (AttackTargetChooser) activeCardChooser;
                                    commandSender.sendCommand(new AttackMonsterCommand(attackTargetChooser.getAttack_MonsterCardIndex(), cardIndex));
                                    cardChooserManager.stop();
                                }
                                else if(activeCardChooser instanceof ThrowingOffChooser){
                                    ThrowingOffChooser throwingOffChooser = (ThrowingOffChooser) activeCardChooser;
                                    Game game = cardInfo.getCard().getGame();
                                    if(cardInfo.getCard().getOwner().getHand().size() <= (game.getGameMode().getMaximumHandCards(game.getCurrentPlayer()) + 1)){
                                        cardChooserManager.stop();
                                    }
                                    commandSender.sendCommand(new ThrowingOffCommand(cardIndex));
                                }
                                panCard.setHighlighted(false);
                            }
                        }
                        else if(isEnabled){
                            if(cardInfo.getCard() != null){
                                prepareSelectedCardPopupMenu(cardInfo);
                                popupMenuSelectedCard.show(panCard, evt.getX(), evt.getY());
                            }
                        }
                    }
                }

                @Override
                public void mouseEntered(MouseEvent evt){
                    super.mouseEntered(evt);
                    FrontendCardInfo cardInfo = getCardInfo(panCard);
                    boolean isHighlighted = false;
                    CardChooser activeCardChooser = cardChooserManager.getActiveCardChooser();
                    if((activeCardChooser != null) && activeCardChooser.isValidChoice(cardInfo.getCard())){
                        isHighlighted = true;
                    }
                    panCard.setHighlighted(isHighlighted);
                    Card card = cardInfo.getCard();
                    if(card != null){
                        if(panCard.isVisibleToUser()){
                            panCardInfo.showCard(cardInfo.getCard());
                        }
                    }
                }

                @Override
                public void mouseExited(MouseEvent evt){
                    super.mouseExited(evt);
                    panCard.setHighlighted(false);
                    panCardInfo.reset();
                }
            });
        }
    }
    
    private void sendSpellCastIfChoosingHasFinished(){
        SpellParameterChooser spellParameterChooser = (SpellParameterChooser) cardChooserManager.getActiveCardChooser();
        if(spellParameterChooser.isFinished()){
            switch(spellParameterChooser.getType()){
                case MONSTER_SPELL:
                    int monsterCardIndex = spellParameterChooser.getChooseInformation()[0];
                    int spellIndex = spellParameterChooser.getChooseInformation()[1];
                    commandSender.sendCommand(new CastMonsterSpellCommand(monsterCardIndex, spellIndex, spellParameterChooser.getSpell_ManaIndices(), spellParameterChooser.getSpellParameters()));
                    break;
                
                case SPELL:
                    int handCardIndex = spellParameterChooser.getChooseInformation()[0];
                    commandSender.sendCommand(new CastSpellCommand(handCardIndex, spellParameterChooser.getSpell_ManaIndices(), spellParameterChooser.getSpellParameters()));
                    break;
            }
            cardChooserManager.stop();
        }
    }
    
    private void prepareSelectedCardPopupMenu(final FrontendCardInfo cardInfo){
        popupMenuSelectedCard.removeAll();
        Card card = cardInfo.getCard();
        if(card instanceof MonsterCard){
            final MonsterCard monsterCard = (MonsterCard) card;
            if(cardInfo.getCardPosition().getZone() == CardPosition.Zone.HAND){
                JMenuItem menItemSummon = new JMenuItem("Summon");
                menItemSummon.addActionListener(new ActionListener(){

                    @Override
                    public void actionPerformed(ActionEvent evt){
                        int handCardIndex = cardInfo.getCardPosition().getIndex();
                        if(monsterCard.getSummonCost().getMana().isEmpty()){
                            commandSender.sendCommand(new SummonMonsterCommand(handCardIndex, new int[0]));
                        }
                        else{
                            cardChooserManager.startChoosing_SummonMonsterCost(playerIndex, monsterCard, handCardIndex);
                        }
                    }
                });
                menItemSummon.setEnabled(monsterCard.getOwner().canPay(monsterCard.getSummonCost(), monsterCard));
                popupMenuSelectedCard.add(menItemSummon);
            }
            else if(cardInfo.getCardPosition().getZone() == CardPosition.Zone.MONSTER){
                final boolean isEnemyMonsterZoneEmpty = card.getOwner().getEnemy().getMonsters().isEmpty();
                JMenuItem menItemAttack = new JMenuItem("Attack " + (isEnemyMonsterZoneEmpty?" Player":" Monster"));
                menItemAttack.addActionListener(new ActionListener(){

                    @Override
                    public void actionPerformed(ActionEvent evt){
                        if(isEnemyMonsterZoneEmpty){
                            commandSender.sendCommand(new AttackPlayerCommand(cardInfo.getCardPosition().getIndex()));
                        }
                        else{
                            cardChooserManager.startChoosing_AttackTarget(playerIndex, cardInfo.getCardPosition().getIndex());
                        }
                    }
                });
                menItemAttack.setEnabled(monsterCard.canAttack());
                popupMenuSelectedCard.add(menItemAttack);
                if(monsterCard.getSpells().length > 0){
                    popupMenuSelectedCard.addSeparator();
                    for(int i=0;i<monsterCard.getSpells().length;i++){
                        final int spellIndex = i;
                        final Spell spell = monsterCard.getSpells()[i];
                        JMenuItem menItemSpell = new JMenuItem("Cast Spell: " + spell.getDescription().getTitle());
                        menItemSpell.addActionListener(new ActionListener(){

                            @Override
                            public void actionPerformed(ActionEvent evt){
                                if(spell.getCost().getMana().isEmpty()){
                                    cardChooserManager.startChoosing_MonsterSpellParameter(spell, cardInfo.getCardPosition().getIndex(), spellIndex, new int[0]);
                                    sendSpellCastIfChoosingHasFinished();
                                }
                                else{
                                    cardChooserManager.startChoosing_MonsterSpellCost(playerIndex, spell, cardInfo.getCardPosition().getIndex(), spellIndex);
                                }
                            }
                        });
                        menItemSpell.setEnabled(monsterCard.getOwner().canCast(spell));
                        popupMenuSelectedCard.add(menItemSpell);
                    }
                }
            }
        }
        if(cardInfo.getCardPosition().getZone() == CardPosition.Zone.HAND){
            if(card instanceof ManaProvider){
                if(popupMenuSelectedCard.getComponentCount() > 0){
                    popupMenuSelectedCard.addSeparator();
                }
                JMenuItem menItemAdd = new JMenuItem("Add Mana");
                menItemAdd.addActionListener(new ActionListener(){

                    @Override
                    public void actionPerformed(ActionEvent evt){
                        commandSender.sendCommand(new AddManaCommand(cardInfo.getCardPosition().getIndex()));
                    }
                });
                menItemAdd.setEnabled(card.getOwner().canAddMana());
                popupMenuSelectedCard.add(menItemAdd);
            }
            if(card instanceof SpellCard){
                SpellCard spellCard = (SpellCard) card;
                if(popupMenuSelectedCard.getComponentCount() > 0){
                    popupMenuSelectedCard.addSeparator();
                }
                final Spell spell = spellCard.getSpell();
                JMenuItem menItemSpell = new JMenuItem("Cast Spell");
                menItemSpell.addActionListener(new ActionListener(){

                    @Override
                    public void actionPerformed(ActionEvent evt){
                        if(spell.getCost().getMana().isEmpty()){
                            cardChooserManager.startChoosing_SpellParameter(spell, cardInfo.getCardPosition().getIndex(), new int[0]);
                            sendSpellCastIfChoosingHasFinished();
                        }
                        else{
                            cardChooserManager.startChoosing_SpellCost(playerIndex, spell, cardInfo.getCardPosition().getIndex());
                        }
                    }
                });
                menItemSpell.setEnabled(spellCard.getOwner().canCast(spell));
                popupMenuSelectedCard.add(menItemSpell);
            }
        }
    }
    
    private FrontendCardInfo getCardInfo(PanCard panCard){
        return buttonsCardsMap.get(panCard);
    }
    
    public void setPlayerIndex(int playerIndex){
        this.playerIndex = playerIndex;
    }

    public void setEnabled(boolean isEnabled){
        this.isEnabled = isEnabled;
    }
}
